﻿using RegexBuilder.Attributes;
using RegexBuilder.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace RegexBuilder.Extentions
{
    /// <summary>
    /// CharSetEnum的扩展方法类，用于解析
    /// </summary>
    internal static class CharSetEnumExtention
    {
        /// <summary>
        /// 枚举值对应的正则表达式
        /// </summary>
        private static Dictionary<CharSetEnum, string> regexStringDictionary;
        /// <summary>
        /// 特殊匹配符号
        /// </summary>

        private static string[] Special = new[] { @".", @"\w", @"\W", @"\b", @"\B", @"\d", @"\D" };
        /// <summary>
        /// 静态构造，初始化字典
        /// </summary>
        static CharSetEnumExtention()
        {
            var s = typeof(CharSetEnum)
                 .GetFields(BindingFlags.Static | BindingFlags.Public)
                 .Select(f =>
                 {
                     var temp = f.GetCustomAttributes().Where(x => x.GetType() == typeof(RegexStringAttribute)).FirstOrDefault();
                     return new KeyValuePair<CharSetEnum, string>((CharSetEnum)f.GetValue(null), (temp as RegexStringAttribute)?.RegexString ?? "");
                 })
                 .Where(x=>x.Value!=String.Empty);
            regexStringDictionary = new Dictionary<CharSetEnum, string>(s);
        }
        /// <summary>
        /// 获取枚举值组合获得正则字符串
        /// </summary>
        /// <param name="CharSet">枚举组合</param>
        /// <returns>字符串</returns>
        /// <exception cref="ArgumentException">无法解析出枚举组合</exception>
        public static string GetPattern(this CharSetEnum CharSet, string includeCharString, string excludeCharString)
        {
            //分解枚举组合，解析低32位，得到正则字符串列表
            Func<ulong, IEnumerable<string>> regexStrings = charsetflags => Enumerable.Range(0, 32)
                   .Where(x => (charsetflags & (ulong)1 << x) > 0)
                   .Select(x => (CharSetEnum)(1 << x))
                   .Select(x => x.GetRegexString());

            //可直接解析的枚举值或组合
            var regexSring = CharSet.GetRegexString();
            if (!string.IsNullOrEmpty(regexSring))
            {
                if (regexSring.StartsWith(@"\") && regexSring.Length == 2)
                {
                    return regexSring;
                }
                else if (Special.Contains(regexSring))
                {
                    return regexSring;
                }
                else
                {
                    return $"[{regexSring}]";
                }

            }

            //分解枚举高低位
            var heighFlags = (ulong)CharSet >> 32;
            var lowFlags = (ulong)CharSet & UInt32.MaxValue;
            //高低位重合时匹配全字符
            if ((heighFlags & lowFlags) > 0)
            {
                return CharSetEnum.All.GetRegexString();
            }
            else
            {
                //分别获取高低位的字符串
                var expects = regexStrings(heighFlags);
                var includes = regexStrings(lowFlags);

                //拼接结果
                if (expects.Count() > 0 && includes.Count() > 0)
                {
                    return $"([^{string.Join(null, expects)}{excludeCharString}]|[{string.Join(null, includes)}{includeCharString}])";
                }
                else if (expects.Count() > 0)
                {
                    return $"[^{string.Join(null, expects)}{excludeCharString}]";
                }
                else if (includes.Count() > 0)
                {
                    return $"[{string.Join(null, includes)}{includeCharString}]";
                }
                else
                {
                    throw new ArgumentException();
                }
            }

        }

        /// <summary>
        /// 获取枚举值正则字符串
        /// </summary>
        /// <param name="CharSet">枚举值</param>
        /// <returns></returns>
        internal static string GetRegexString(this CharSetEnum CharSet)
        {
            if (regexStringDictionary.TryGetValue(CharSet, out string temp))
            {
                return temp;
            }
            else
            {
                return string.Empty;
            }
        }
    }
}
